package com.mcx.modules.controller;

import com.mcx.modules.PlayerEntity;
import com.mcx.modules.PlayerMgr;
import com.mcx.modules.common.EventDispatcher;
import com.mcx.modules.common.OnActionHandler;
import com.mcx.modules.data.localdata.create.*;
import com.mcx.modules.data.localdata.dbmodel.SkillLevelDBModelExt;
import com.mcx.modules.proto.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Component
public class WorldMapSceneController {
    private HashMap<Integer, WorldMapSceneEntity> m_WorldMapSceneControllerDic;

    /// <summary>
    /// 初始化
    /// </summary>
    public void Init()
    {
        //初始化世界地图场景控制器
        InitWorldMapScene();

        //客户端发送角色已经进入世界地图场景消息
        EventDispatcher.getInstance().AddEventListener(ProtoCodeDef.WorldMap_RoleAlreadyEnter, new OnActionHandler(){
            @Override
            public void dispatch(PlayerEntity player, byte[] buffer) throws IOException {
                OnWorldMapRoleAlreadyEnter(player, buffer);
            }
        });

        //客户端发送当前角色移动消息
        EventDispatcher.getInstance().AddEventListener(ProtoCodeDef.WorldMap_CurrRoleMove, new OnActionHandler(){
            @Override
            public void dispatch(PlayerEntity player, byte[] buffer) throws IOException {
                OnWorldMapCurrRoleMove(player, buffer);
            }
        });

        //客户端发送角色使用技能消息
        EventDispatcher.getInstance().AddEventListener(ProtoCodeDef.WorldMap_CurrRoleUseSkill, new OnActionHandler(){
            @Override
            public void dispatch(PlayerEntity player, byte[] buffer) throws IOException {
                OnWorldMapCurrRoleUseSkill(player, buffer);
            }
        });

        //客户端发送角色复活消息
        EventDispatcher.getInstance().AddEventListener(ProtoCodeDef.WorldMap_CurrRoleResurgence, new OnActionHandler(){
            @Override
            public void dispatch(PlayerEntity player, byte[] buffer) throws IOException {
                OnWorldMapCurrRoleResurgence(player, buffer);
            }
        });
    }

    /// <summary>
    /// 初始化世界地图场景控制器
    /// </summary>
    private void InitWorldMapScene()
    {
        List<WorldMapEntity> lst = WorldMapDBModel.getInstance().GetList();

        if (lst == null) return;
        m_WorldMapSceneControllerDic = new HashMap<>();

        for (int i = 0; i < lst.size(); i++)
        {
            WorldMapEntity entity = lst.get(i);
            log.info(String.format("世界地图%s初始化完毕", entity.Name));
            WorldMapSceneEntity ctrl = new WorldMapSceneEntity(entity.Id);
            m_WorldMapSceneControllerDic.put(entity.Id, ctrl);
        }
    }

    /// <summary>
    /// 角色进入某个场景
    /// </summary>
    /// <param name="role"></param>
    /// <param name="worldMapSceneId"></param>
    public void RoleEnter(PlayerEntity playerEntity, int worldMapSceneId)
    {
        if (!m_WorldMapSceneControllerDic.containsKey(worldMapSceneId)) return;

        m_WorldMapSceneControllerDic.get(worldMapSceneId).RoleEnter(playerEntity);
    }

    /// <summary>
    /// 角色离开某个场景
    /// </summary>
    /// <param name="roleId"></param>
    /// <param name="worldMapSceneId"></param>
    public void RoleLeave(int roleId, int worldMapSceneId) throws IOException {
        if (!m_WorldMapSceneControllerDic.containsKey(worldMapSceneId)) return;
        m_WorldMapSceneControllerDic.get(worldMapSceneId).RoleLeave(roleId);

        log.info("roleId=" + roleId);
        log.info("worldMapSceneId=" + worldMapSceneId);

        //通知同场景其他玩家 我离开了
        NotifyOtherRole_RoleLeave(worldMapSceneId, roleId);
    }

    /// <summary>
    /// 获取某个场景所有角色
    /// </summary>
    /// <returns></returns>
    public List<PlayerEntity> GetRoleList(int worldMapSceneId)
    {
        if (!m_WorldMapSceneControllerDic.containsKey(worldMapSceneId)) return null;
        return m_WorldMapSceneControllerDic.get(worldMapSceneId).GetRoleList();
    }

    //================================================================================== 角色进入 离开场景 监听回调
    /// <summary>
    /// 客户端发送角色已经进入世界地图场景消息
    /// </summary>
    /// <param name="role"></param>
    /// <param name="buffer"></param>
    private void OnWorldMapRoleAlreadyEnter(PlayerEntity playerEntity, byte[] buffer) throws IOException {
        WorldMap_RoleAlreadyEnterProto proto = WorldMap_RoleAlreadyEnterProto.GetProto(buffer);

        int sceneId = proto.TargetWorldMapSceneId;

        log.info("角色要离开的场景=" + playerEntity.getLastInWorldMapId());

        //1.离开上一个场景
        RoleLeave(playerEntity.getId(), playerEntity.getPrevWorldMapId());

        playerEntity.setPrevWorldMapId(sceneId);
        playerEntity.setLastInWorldMapId(sceneId);
        playerEntity.setLastInWorldMapPos(String.format("%f_%f_%f_%f", proto.RolePosX, proto.RolePosY, proto.RolePosZ, proto.RoleYAngle));

        //2.发送当前场景中的其他玩家
        SendCurrSceneInitRole(playerEntity, sceneId);

        //3.进入当前场景
        RoleEnter(playerEntity, sceneId);

        //4.通知同场景的其他玩家 告诉他们我来了
        NotifyOtherRole_RoleEnter(sceneId, playerEntity.getId());
    }

    /// <summary>
    /// 服务器发送当前场景初始化的角色
    /// </summary>
    /// <param name="role"></param>
    /// <param name="sceneId"></param>
    private void SendCurrSceneInitRole(PlayerEntity playerEntity, int sceneId) throws IOException {
        WorldMap_InitRoleProto proto = new WorldMap_InitRoleProto();

        List<PlayerEntity> lst = GetRoleList(sceneId);

        proto.RoleCount = lst.size();
        proto.ItemList = new ArrayList<WorldMap_InitRoleProto.RoleItem>();

        for (int i = 0; i < lst.size(); i++)
        {
            WorldMap_InitRoleProto.RoleItem item = new WorldMap_InitRoleProto.RoleItem();
            item.RoleId = lst.get(i).getId();

            item.RoleLevel = lst.get(i).getLevel();
            item.RoleNickName = lst.get(i).getNickName();

            String[] arr = lst.get(i).getLastInWorldMapPos().split("_");
            item.RolePosX = Float.valueOf(arr[0]);
            item.RolePosY = Float.valueOf(arr[1]);
            item.RolePosZ = Float.valueOf(arr[2]);
            item.RoleYAngle = Float.valueOf(arr[3]);

            proto.ItemList.add(item);
        }

        playerEntity.Client_Socket.SendMsg(proto.ToArray());
    }

    /// <summary>
    /// 通知同场景的其他玩家 告诉他们我来了
    /// </summary>
    /// <param name="worldMapSceneId"></param>
    /// <param name="enterRoleId"></param>
    private void NotifyOtherRole_RoleEnter(int worldMapSceneId, int enterRoleId) throws IOException {
        List<PlayerEntity> lst = GetRoleList(worldMapSceneId);
        if (lst != null && lst.size() > 0)
        {
            for (int i = 0; i < lst.size(); i++)
            {
                if (lst.get(i).getId() == enterRoleId) continue;
                SendToOtherRole_RoleEnter(lst.get(i), enterRoleId);
            }
        }
    }

    /// <summary>
    /// 通知同场景的其他玩家 告诉他们我来了
    /// </summary>
    /// <param name="otherRole"></param>
    /// <param name="enterRoleId"></param>
    private void SendToOtherRole_RoleEnter(PlayerEntity otherPlayerEntity, int enterRoleId) throws IOException {
        WorldMap_OtherRoleEnterProto proto = new WorldMap_OtherRoleEnterProto();
        proto.RoleId = enterRoleId;
        PlayerEntity enterplayerEntity = PlayerMgr.getInstance().GetRole(enterRoleId);
        if (enterplayerEntity != null)
        {
            proto.RoleLevel = enterplayerEntity.getLevel();
            proto.RoleNickName = enterplayerEntity.getNickName();

            String[] arr = enterplayerEntity.getLastInWorldMapPos().split("_");
            proto.RolePosX = Float.valueOf(arr[0]);
            proto.RolePosY = Float.valueOf(arr[1]);
            proto.RolePosZ = Float.valueOf(arr[2]);
            proto.RoleYAngle = Float.valueOf(arr[3]);
        }

        otherPlayerEntity.Client_Socket.SendMsg(proto.ToArray());
    }

    /// <summary>
    /// 通知同场景的其他玩家 我离开了
    /// </summary>
    /// <param name="worldMapSceneId"></param>
    /// <param name="levalRoleId"></param>
    private void NotifyOtherRole_RoleLeave(int worldMapSceneId, int levalRoleId) throws IOException {
        List<PlayerEntity> lst = GetRoleList(worldMapSceneId);
        if (lst != null && lst.size() > 0)
        {
            for (int i = 0; i < lst.size(); i++)
            {
                if (lst.get(i).getId() == levalRoleId) continue;
                SendToOtherRole_RoleLeave(lst.get(i), levalRoleId);
            }
        }
    }

    /// <summary>
    /// 通知同场景的其他玩家 我离开了
    /// </summary>
    /// <param name="otherRole"></param>
    /// <param name="levalRoleId"></param>
    private void SendToOtherRole_RoleLeave(PlayerEntity otherPlayerEntity, int levalRoleId) throws IOException {
        WorldMap_OtherRoleLeaveProto proto = new WorldMap_OtherRoleLeaveProto();
        proto.RoleId = levalRoleId;
        otherPlayerEntity.Client_Socket.SendMsg(proto.ToArray());
    }

    //======================================================================角色移动
    /// <summary>
    /// 客户端发送当前角色移动消息
    /// </summary>
    /// <param name="role"></param>
    /// <param name="buffer"></param>
    private void OnWorldMapCurrRoleMove(PlayerEntity playerEntity, byte[] buffer) throws IOException {
        WorldMap_CurrRoleMoveProto proto = WorldMap_CurrRoleMoveProto.GetProto(buffer);
        //log.info("我的服务器时间=" + proto.ServerTime);
        //log.info("服    务器时间=" + DateTime.Now.ToUniversalTime().Ticks / 10000);

        List<PlayerEntity> lst = GetRoleList(playerEntity.getLastInWorldMapId());
        if (lst != null && lst.size() > 0)
        {
            for (int i = 0; i < lst.size(); i++)
            {
                if (lst.get(i).getId() == playerEntity.getId()) continue;

                WorldMap_OtherRoleMoveProto myProto = new WorldMap_OtherRoleMoveProto();

                myProto.RoleId = playerEntity.getId();
                myProto.TargetPosX = proto.TargetPosX;
                myProto.TargetPosY = proto.TargetPosY;
                myProto.TargetPosZ = proto.TargetPosZ;
                myProto.ServerTime = proto.ServerTime;
                myProto.NeedTime = proto.NeedTime;



                lst.get(i).Client_Socket.SendMsg(myProto.ToArray());
            }
        }
    }

    /// <summary>
    /// 被攻击的角色列表
    /// </summary>
    private ArrayList<WorldMap_OtherRoleUseSkillProto.BeAttackItem> m_BeAttackItemList = new ArrayList<WorldMap_OtherRoleUseSkillProto.BeAttackItem>();

    /// <summary>
    /// 被攻击后死亡的角色列表
    /// </summary>
    private ArrayList<Integer> m_DieRoleList = new ArrayList<Integer>();

    /// <summary>
    /// 当前角色使用技能
    /// </summary>
    /// <param name="role"></param>
    /// <param name="buffer"></param>
    private void OnWorldMapCurrRoleUseSkill(PlayerEntity playerEntity, byte[] buffer) throws IOException {
        m_BeAttackItemList.clear();
        m_DieRoleList.clear();

        WorldMap_CurrRoleUseSkillProto proto = WorldMap_CurrRoleUseSkillProto.GetProto(buffer);

        int attackRoleId = playerEntity.getId(); //攻击者ID
        int skillId = proto.SkillId; //使用的技能编号
        int skillLevel = proto.SkillLevel; //使用的技能等级
        float rolePosX = proto.RolePosX;
        float rolePosY = proto.RolePosY;
        float rolePosZ = proto.RolePosZ;
        float roleYAngle = proto.RoleYAngle;

        int beAttackCount = proto.BeAttackCount; //被攻击者数量
        for (int i = 0; i < beAttackCount; i++)
        {
            int beAttackRoleId = proto.ItemList.get(i).BeAttackRoleId; //被攻击者编号

            boolean isCri = false; //是否暴击
            int hurtValue = 0; //伤害值

            PlayerEntity attackRole = PlayerMgr.getInstance().GetRole(attackRoleId); //攻击者
            //攻击者的MP要减少
            SkillLevelEntity skillLevelEntity = SkillLevelDBModelExt.getInstance().GetEntityBySkillIdAndLevel(skillId, skillLevel);
//            attackRole.CurrMP -= skillLevelEntity.SpendMP;
//            attackRole.CurrMP = attackRole.CurrMP < 0 ? 0 : attackRole.CurrMP;

            PlayerEntity beAttackRole = PlayerMgr.getInstance().GetRole(beAttackRoleId); //被攻击者

            //服务器端计算伤害
            ArrayList<Object> ret = CalculateHurtValue(attackRole, beAttackRole, skillId, skillLevel);
            isCri = (boolean)ret.get(0);
            hurtValue = (int)ret.get(1);

            WorldMap_OtherRoleUseSkillProto.BeAttackItem item = new WorldMap_OtherRoleUseSkillProto.BeAttackItem();
            item.BeAttackRoleId = beAttackRoleId; //被攻击者id
            item.IsCri = (byte)(isCri ? 1 : 0); //是否暴击
            item.ReduceHp = hurtValue; //伤害

//            beAttackRole.CurrHP -= hurtValue; //服务器端的被攻击者 减少血量
//            if (beAttackRole.CurrHP <= 0)
//            {
//                //说明角色已经死亡
//                beAttackRole.CurrHP = 0;
//                m_DieRoleList.add(beAttackRoleId);
//            }
//
//            m_BeAttackItemList.add(item);
        }

        //要把受伤消息 发给同场景的玩家 玩家A 要能看到 B和C在战斗 即便A只是旁观者
        //================告诉同场景的玩家 给他们发消息 包括使用技能的玩家 被攻击者 和 旁观者
        {
            List<PlayerEntity> lst = GetRoleList(playerEntity.getLastInWorldMapId());
            if (lst != null && lst.size() > 0)
            {
                for (int i = 0; i < lst.size(); i++)
                {
                    SendOtherRoleUseSkill(lst.get(i), attackRoleId, skillId, skillLevel, rolePosX, rolePosY, rolePosZ, roleYAngle, m_BeAttackItemList);
                }
            }
        }
        //===========================

        //要把死亡消息 发给同场景的玩家 玩家A 要能看到 B和C在战斗 即便A只是旁观者
        {
            List<PlayerEntity> lst = GetRoleList(playerEntity.getLastInWorldMapId());
            if (lst != null && lst.size() > 0)
            {
                for (int i = 0; i < lst.size(); i++)
                {
                    SendOtherRoleDie(lst.get(i), attackRoleId, m_DieRoleList);
                }
            }
        }
    }

    //==============================================================服务器广播 其他角色使用技能消息
    /// <summary>
    /// 服务器广播 其他角色使用技能消息
    /// </summary>
    /// <param name="role"></param>
    private void SendOtherRoleUseSkill(PlayerEntity playerEntity,
                                       int attackRoleId, int skillId, int skillLevel, float rolePosX, float rolePosY, float rolePosZ, float roleYAngle,
                                       ArrayList<WorldMap_OtherRoleUseSkillProto.BeAttackItem> lst) throws IOException {
        WorldMap_OtherRoleUseSkillProto proto = new WorldMap_OtherRoleUseSkillProto();
        proto.AttackRoleId = attackRoleId;
        proto.SkillId = skillId;
        proto.SkillLevel = skillLevel;
        proto.RolePosX = rolePosX;
        proto.RolePosY = rolePosY;
        proto.RolePosZ = rolePosZ;
        proto.RoleYAngle = roleYAngle;

        proto.BeAttackCount = lst.size();
        proto.ItemList = lst;

        playerEntity.Client_Socket.SendMsg(proto.ToArray());
    }

    /// <summary>
    /// 服务器发送角色死亡消息
    /// </summary>
    /// <param name="role"></param>
    /// <param name="lst"></param>
    private void SendOtherRoleDie(PlayerEntity playerEntity, int attackRoleId, ArrayList<Integer> lst) throws IOException {
        WorldMap_OtherRoleDieProto proto = new WorldMap_OtherRoleDieProto();
        proto.AttackRoleId = attackRoleId; //攻击者是谁
        proto.DieCount = lst.size();
        proto.RoleIdList = lst; //哪些人死亡

        playerEntity.Client_Socket.SendMsg(proto.ToArray());
    }

    /// <summary>
    /// 服务器端计算伤害
    /// </summary>
    /// <param name="currRole"></param>
    /// <param name="enemy"></param>
    /// <param name="skillId"></param>
    /// <param name="skillLevel"></param>
    /// <param name="isCri"></param>
    /// <param name="hurtValue"></param>
    private ArrayList<Object> CalculateHurtValue(PlayerEntity currRole, PlayerEntity enemy, int skillId, int skillLevel)
    {
        boolean isCri = false;
        int hurtValue = 0;
        if (currRole == null || enemy == null) return null;

        SkillEntity skillEntity = SkillDBModel.getInstance().Get(skillId);
        SkillLevelEntity skillLevelEntity = SkillLevelDBModelExt.getInstance().GetEntityBySkillIdAndLevel(skillId, skillLevel);
        if (skillEntity == null) return null;

        //计算伤害
        //1.攻击数值 = 攻击方的综合战斗力 * （技能的伤害倍率 * 0.01f）
//        float attackValue = currRole.Fighting * (skillLevelEntity.HurtValueRate * 0.01f);

        //2.基础伤害 = 攻击数值 * 攻击数值 / (攻击数值 + 被攻击方的防御)
//        float baseHurt = attackValue * attackValue / (attackValue + enemy.Defense);

        //3.暴击概率 = 0.05f + (攻击方暴击/(攻击方暴击+防御方抗性)) * 0.1f;
//        float cri = 0.05f + (currRole.Cri / (currRole.Cri + enemy.Res)) * 0.1f;

        //暴击概率 = 暴击概率>0.5f?0.5f:暴击概率
//        cri = cri > 0.5f ? 0.5f : cri;


        //4.是否暴击 = 0-1的随机数 < =暴击概率
//        isCri = new Random().NextDouble() <= cri;

        //5.暴击伤害倍率 = 有暴击?1.5f ：1f;
        float criHrut = isCri ? 1.5f : 1f;

        //6.随机数 = 0.9f-1.1f之间
//        int random = new Random().Next(9000, 11000);
//        float frandom = random * 0.0001f;

        //7.最终伤害 = 基础伤害 * 暴击伤害倍率 * 随机数
//        hurtValue = (int)Math.Round(baseHurt * criHrut * frandom);
        hurtValue = hurtValue < 1 ? 1 : hurtValue;
        int finalHurtValue = hurtValue;
        return new ArrayList<Object>(){{ add(isCri); add(finalHurtValue); }};
    }

    /// <summary>
    /// 客户端发送角色复活消息
    /// </summary>
    /// <param name="role"></param>
    /// <param name="buffer"></param>
    private void OnWorldMapCurrRoleResurgence(PlayerEntity playerEntity, byte[] buffer) throws IOException {
        WorldMap_CurrRoleResurgenceProto proto = new WorldMap_CurrRoleResurgenceProto();

        int type = proto.Type; //以后此处处理逻辑 比如扣除元宝等

        //修改要复活的角色HP和MP
        PlayerEntity resurgenceRole = PlayerMgr.getInstance().GetRole(playerEntity.getId()); //要复活者
//        resurgenceRole.CurrHP = resurgenceRole.MaxHP;
//        resurgenceRole.CurrMP = resurgenceRole.MaxMP;

        List<PlayerEntity> lst = GetRoleList(playerEntity.getLastInWorldMapId());
        if (lst != null && lst.size() > 0)
        {
            for (int i = 0; i < lst.size(); i++)
            {
                SendRoleResurgence(lst.get(i), playerEntity.getId());
            }
        }
    }

    /// <summary>
    /// 服务器发送角色复活消息
    /// </summary>
    /// <param name="role"></param>
    /// <param name="roleId"></param>
    private void SendRoleResurgence(PlayerEntity playerEntity, int roleId) throws IOException {
        WorldMap_OtherRoleResurgenceProto proto = new WorldMap_OtherRoleResurgenceProto();
        proto.RoleId = roleId;
        playerEntity.Client_Socket.SendMsg(proto.ToArray());
    }
}
